Conversation
…ox library This commit refines the EIP-712 type hashes for meta-transactions in the EngineBlox library to align with standard conventions, ensuring compatibility with typed-data signing methods. The documentation has been updated to clarify that integrators can use either typed-data signing or raw hash signing, emphasizing the importance of matching domain and type definitions for accurate digest reproduction. Additionally, the changes ensure that the type hashes remain synchronized across relevant components, enhancing the overall integrity and usability of the meta-transaction framework.
This commit increments the version number to 1.0.0-alpha.18 in the package.json files for the main project, SDK, and contracts. These updates ensure consistency across the project and prepare the codebase for the next release cycle.
This commit adds a check to ensure that the walletClient has an active account before proceeding with typed-data signing in the MetaTransactionSigner class. This enhancement improves error handling by providing a clear message when the account is not available, ensuring that the signing process is robust and reliable. The change aligns with best practices for validating prerequisites in the signing workflow.
📝 WalkthroughWalkthroughThis PR consolidates contract version constants into a single Changes
Sequence Diagram(s)sequenceDiagram
participant Signer as MetaTransactionSigner
participant Wallet as WalletClient
participant Verifier as SDKVerifier
participant Contract as EngineBlox (on-chain)
Signer->>Signer: buildTypedDataMessage(metaTx)
Signer->>Wallet: signTypedData(domain, types, message)
Wallet-->>Signer: signature
Signer->>Verifier: verifySignature(message, signature)
Verifier-->>Signer: signerAddress
Signer->>Contract: submit meta-transaction + signature
Contract->>Contract: generateMessageHash() / recoverSigner()
Contract-->>Signer: accept or reject transaction
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
sdk/typescript/utils/metaTx/metaTransaction.tsx (1)
64-65: Please keep this exported typed-data path actually typed.
buildTypedDataMessagewidens the public message shape toRecord<string, unknown>, andsignMetaTransactionWithWallethas to cast thesignTypedDatapayload toany. That removes the compile-time check that the SDK's typed-data fields still match the Solidity schema, which is exactly the guardrail this new public API needs.As per coding guidelines,
**/*.{ts,tsx}: Expose a type-safe TypeScript SDK (interfaces and classes) for contract interaction (e.g., Viem clients) andValidate presence of wallet client and handle async contract calls using typed methods, returning typed results.Also applies to: 203-209
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/typescript/utils/metaTx/metaTransaction.tsx` around lines 64 - 65, The exported buildTypedDataMessage should return a concrete typed shape instead of Record<string, unknown>; define a TypeScript interface (e.g., MetaTransactionTypedData or EIP712MetaTransaction) that matches the EIP-712 domain/types/message schema used by the Solidity contract, change buildTypedDataMessage(metaTx: MetaTransaction): MetaTransactionTypedData, and update signMetaTransactionWithWallet to accept/sign that typed payload (remove the any cast) so the compiler enforces the fields; reference the functions buildTypedDataMessage and signMetaTransactionWithWallet and the new interface name when making these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 3: The npm dist-tags used in the publish scripts are out of sync with the
package version 1.0.0-alpha.18; update the publish commands so the dist-tag
passed to npm (references: the "publish:contracts" and "publish:sdk" script
entries in package.json) uses "alpha.18" (or derive from the package.json
version) instead of "alpha.17"; locate the "publish:contracts" and "publish:sdk"
script values and replace the "--tag alpha.17" token with "--tag alpha.18" (or
refactor to compute the tag from the version string) so published artifacts and
release metadata remain consistent.
---
Nitpick comments:
In `@sdk/typescript/utils/metaTx/metaTransaction.tsx`:
- Around line 64-65: The exported buildTypedDataMessage should return a concrete
typed shape instead of Record<string, unknown>; define a TypeScript interface
(e.g., MetaTransactionTypedData or EIP712MetaTransaction) that matches the
EIP-712 domain/types/message schema used by the Solidity contract, change
buildTypedDataMessage(metaTx: MetaTransaction): MetaTransactionTypedData, and
update signMetaTransactionWithWallet to accept/sign that typed payload (remove
the any cast) so the compiler enforces the fields; reference the functions
buildTypedDataMessage and signMetaTransactionWithWallet and the new interface
name when making these changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 16a543c7-a007-404a-8050-2279ab55618f
📒 Files selected for processing (6)
contracts/core/lib/EngineBlox.solpackage.jsonpackage/package.jsonsdk/typescript/package.jsonsdk/typescript/utils/metaTx/metaTransaction.tsxtest/foundry/helpers/TestHelpers.sol
| { | ||
| "name": "Bloxchain", | ||
| "version": "1.0.0-alpha.17", | ||
| "version": "1.0.0-alpha.18", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n package.json | head -60Repository: PracticalParticle/Bloxchain-Protocol
Length of output: 3961
Sync the npm dist-tags with this version bump.
The manifest is now 1.0.0-alpha.18, but publish:contracts and publish:sdk still publish under --tag alpha.17. That will push the new artifacts onto the old dist-tag and leave the release metadata inconsistent.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 3, The npm dist-tags used in the publish scripts are
out of sync with the package version 1.0.0-alpha.18; update the publish commands
so the dist-tag passed to npm (references: the "publish:contracts" and
"publish:sdk" script entries in package.json) uses "alpha.18" (or derive from
the package.json version) instead of "alpha.17"; locate the "publish:contracts"
and "publish:sdk" script values and replace the "--tag alpha.17" token with
"--tag alpha.18" (or refactor to compute the tag from the version string) so
published artifacts and release metadata remain consistent.
…ted files This commit refactors the versioning system in the EngineBlox library by replacing the separate VERSION_MAJOR, VERSION_MINOR, and VERSION_PATCH constants with a single VERSION string formatted as "1.0.0". The changes are reflected across multiple files, including the ABI definitions and TypeScript SDK, ensuring consistency in version representation. Additionally, the sync script has been updated to accommodate this new versioning format, enhancing the overall maintainability and clarity of version management across the codebase.
…commitlint dependencies This commit updates the version in package-lock.json to 1.0.0-alpha.18, ensuring consistency with the latest versioning across the project. Additionally, it upgrades various @commitlint dependencies to version 20.4.4, enhancing the linting capabilities and maintaining alignment with the latest standards. These changes contribute to improved maintainability and reliability in the development workflow.
…r in release-please module This commit modifies the package-lock.json file to add the conventional-commits-filter dependency under the release-please module, ensuring that the project utilizes the latest version (3.0.0) for improved commit message handling. Additionally, it removes the duplicate entry for conventional-commits-filter, streamlining the dependency management and enhancing overall project maintainability.
chore: consolidate versioning approach in EngineBlox library and rela…
… package-lock.json This commit updates the versioning of the OpenZeppelin contracts and upgradeable contracts in both package.json and package-lock.json to a fixed version of 5.4.0. This change ensures consistency across the project dependencies, enhancing maintainability and reducing potential issues related to version mismatches. The previous caret (^) versioning has been replaced with exact versions to prevent unintended upgrades in future installations.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/sync-contract-versions.yml:
- Around line 76-85: The workflow currently only checks and commits changes to
contracts/core/lib/EngineBlox.sol's VERSION constant, but npm run
release:sync-versions also rewrites sdk/typescript/lib/EngineBlox.tsx
(EngineBlox.VERSION), so update the job to detect and commit both files
together: modify the diff/grep check that looks for constant VERSION in
EngineBlox.sol to also include sdk/typescript/lib/EngineBlox.tsx (or check for
VERSION in either file), and ensure the git add/git commit step stages both
contracts/core/lib/EngineBlox.sol and sdk/typescript/lib/EngineBlox.tsx so the
contract VERSION and the SDK EngineBlox.VERSION are committed atomically; keep
the existing safety logic (only allow commit when only the VERSION changes) but
apply it across both files.
In `@sdk/typescript/utils/metaTx/metaTransaction.tsx`:
- Around line 196-200: The domain for EIP-712 signing is using this.chain.id
instead of the chainId embedded in the unsigned payload; update the domain
construction in metaTransaction.tsx (where domain and META_TX_DOMAIN are used)
to set chainId from unsignedMetaTx.message.chainId (or the appropriate chain id
field on unsignedMetaTx) and keep verifyingContract as this.contractAddress so
signTypedData uses the same chain id that produced unsignedMetaTx.message,
preventing mismatched digests when the signer’s this.chain may be stale.
In `@test/foundry/helpers/TestHelpers.sol`:
- Around line 134-142: The test helper currently hardcodes the EIP‑712 version
string ("1.0.0") when building domainSeparator, which will desync from
MetaTxSigner.generateMessageHash/EngineBlox when the base version is bumped;
replace keccak256(bytes("1.0.0")) with a single shared constant reference (e.g.,
keccak256(bytes(PROTOCOL_VERSION)) or the same VERSION/PROTOCOL constant
exported by the contracts/SDK) so TestHelpers.sol uses the canonical version
value, and import or define that constant in TestHelpers.sol and use it in the
abi.encode call with DOMAIN_SEPARATOR_TYPE_HASH, PROTOCOL_NAME_HASH,
block.chainid and verifyingContract to keep byte-for-byte alignment.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b9414b24-bd18-4571-a3e0-8a75e5c75abb
📒 Files selected for processing (8)
.github/workflows/sync-contract-versions.ymlabi/EngineBlox.abi.jsoncontracts/core/lib/EngineBlox.solscripts/sync-versions.cjssdk/typescript/abi/EngineBlox.abi.jsonsdk/typescript/lib/EngineBlox.tsxsdk/typescript/utils/metaTx/metaTransaction.tsxtest/foundry/helpers/TestHelpers.sol
This commit updates the chainId assignment in the MetaTransactionSigner class to ensure it correctly converts the chainId from the unsignedMetaTx parameters to a number. This change enhances the accuracy of the meta-transaction signing process, aligning with best practices for type handling in TypeScript.
This commit updates the GitHub Actions workflow for syncing contract versions to include checks for uncommitted changes in both the Solidity and TypeScript version files. The workflow now verifies that changes are limited to version constants, ensuring a safer update process. Additionally, it improves error messaging for clarity when uncommitted changes are detected, enhancing the overall reliability of the version synchronization process.
…check This commit enhances the timeout configuration in the remote Ganache health check script by introducing a default timeout value and validating the environment variable for custom timeout settings. It also adds warning messages for invalid timeout values, ensuring better clarity and reliability during execution. Additionally, it standardizes variable naming for improved readability in the codebase.
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
scripts/sanity/utils/eip712-signing.cjs (1)
91-106:⚠️ Potential issue | 🟠 MajorMake digest parsing strict instead of coercing it.
_normalizeMessageHex()is still sanitizing, truncating, and padding arbitrary input. BecausegenerateMessageHash()and_signRawDigest()both rely on it, malformed values like0x12zzor oversized hashes can be silently rewritten into a different 32-byte digest and then signed. For EIP-712, this path should reject anything that is not already exactly 32 bytes.🧪 Proposed fix
_normalizeMessageHex(value) { if (value == null || value === '') return null; let hex; try { - if (typeof value === 'string') hex = value; - else if (typeof value === 'bigint') hex = '0x' + value.toString(16); - else hex = this.web3.utils.toHex(value); + if (typeof value === 'string') hex = value; + else hex = this.web3.utils.toHex(value); } catch (_) { - hex = String(value); + return null; } if (!hex || typeof hex !== 'string') return null; - if (!hex.startsWith('0x')) hex = '0x' + hex; - const body = hex.slice(2).replace(/[^0-9a-fA-F]/g, '') || '0'; - if (body.length > 64) hex = '0x' + body.slice(-64); - else hex = '0x' + body.padStart(64, '0'); + if (!hex.startsWith('0x')) hex = `0x${hex}`; return /^0x[0-9a-fA-F]{64}$/.test(hex) ? hex : null; }Also applies to: 181-189
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/utils/eip712-signing.cjs` around lines 91 - 106, The _normalizeMessageHex() helper is currently sanitizing, truncating and padding inputs which can silently convert malformed digests into different 32-byte values; change it to be strict: only accept a string that already matches /^0x[0-9a-fA-F]{64}$/ and return null for everything else (do not coerce bigint/other types, do not pad or truncate), and apply the same strict behavior to the equivalent logic at lines ~181-189; ensure callers generateMessageHash() and _signRawDigest() rely on this strict validation so malformed digests are rejected instead of being rewritten and signed.scripts/sanity/guard-controller/base-test.cjs (2)
1853-1908:⚠️ Potential issue | 🟠 MajorNormalize native-transfer state instead of only checking for presence.
This helper only checks whether the schema exists and whether each role has one required bit. A pre-split permission entry on the same selector will either be left in place because the bit is already present, or hit
ResourceAlreadyExistswhenaddFunctionToRole()tries to “fix” the wrong bitmap without removing it first. That meansensureNativeTransferSchemaAndPermissions()can report success while the native-transfer setup is still incompatible with the split sign/execute model.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/guard-controller/base-test.cjs` around lines 1853 - 1908, The helper ensureNativeTransferSchemaAndPermissions currently only checks presence of schema/permission bits, which can leave pre-split permission entries or incompatible bitmaps in place and cause ResourceAlreadyExists in addFunctionToRole; update ensureNativeTransferSchemaAndPermissions to normalize state for nativeSelector by (1) when a role has any permission entry for nativeSelector, inspect the existing bitmap and if it doesn't exactly match the desired signActions (for ownerRoleHash) or execActions (for broadcasterRoleHash) remove or update that function entry first (e.g., call the role/function removal or update helper before adding) so the role ends up with exactly the expected bits, (2) handle ResourceAlreadyExists from addFunctionToRole by attempting an idempotent reconciliation (remove+re-add or update) rather than treating it as success, and (3) use the existing helpers functionSchemaExists, registerFunction, roleHasPermissionForSelector, addFunctionToRole when locating and normalizing entries for nativeSelector to ensure the schema and both roles are in the correct split sign/execute state.
549-568:⚠️ Potential issue | 🟠 Major
receiptTimeoutMsdoes not bound the blocking time insendSignedTransaction().The
await this.web3.eth.sendSignedTransaction()call waits for the transaction to be mined, not just broadcast. This means the method can block indefinitely on a slow or stuck RPC during that initial await, regardless of thereceiptTimeoutMstimeout. The parameter only limits the subsequent polling loop for the receipt, leaving the mining phase unprotected.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/guard-controller/base-test.cjs` around lines 549 - 568, The code currently awaits this.web3.eth.sendSignedTransaction(...) which can block until mining; instead, trigger the transaction send and capture the transactionHash immediately by using the PromiEvent events (e.g., call this.web3.eth.sendSignedTransaction(signed.rawTransaction).once('transactionHash', txHash => { ... }) or .on('transactionHash', ...)) rather than awaiting the final promise—so in sendTransactionReceiptOnly replace the await on sendSignedTransaction with subscribing to the 'transactionHash' event to obtain transactionHash quickly (use signed.rawTransaction and store transactionHash), then run the existing polling loop using receiptTimeoutMs to wait for the receipt; also handle the sendSignedTransaction error event to surface send errors.
🧹 Nitpick comments (4)
scripts/sanity-sdk/runtime-rbac/rbac-tests.ts (1)
695-713: LGTM - Improved error handling with multi-source message extraction and timeout pattern.The changes correctly handle viem's nested error structure by checking multiple properties (
cause?.shortMessage,cause?.message,shortMessage,message). Adding the "took too long to respond" pattern alongside "Missing or invalid parameters" is appropriate for handling RPC timeout scenarios in constrained environments.Optional: Consider extracting the error message extraction into a helper.
This pattern is duplicated across Steps 2, 4, 5, 7, and 8. A shared helper would reduce duplication:
♻️ Optional refactor suggestion
// Add as a private method in RuntimeRBACTests private extractErrorMessage(error: unknown): string { if (error && typeof error === 'object') { const e = error as Record<string, unknown>; const cause = e.cause as Record<string, unknown> | undefined; return ( cause?.shortMessage ?? cause?.message ?? e.shortMessage ?? e.message ?? '' ).toString(); } return ''; }Then use it in catch blocks:
} catch (step5Error: any) { - const msg = ( - step5Error?.cause?.shortMessage ?? - step5Error?.cause?.message ?? - step5Error?.shortMessage ?? - step5Error?.message ?? - '' - ).toString(); + const msg = this.extractErrorMessage(step5Error);Also note: Step 5 now handles both "Missing or invalid parameters" and "took too long to respond", but Steps 2, 4, 7, and 8 only check for the former. If timeout handling should be consistent across all steps, consider applying the same pattern there.
,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity-sdk/runtime-rbac/rbac-tests.ts` around lines 695 - 713, Summary: duplicate error-message extraction across Steps 2,4,5,7,8 should be centralized and timeout handling normalized. Add a private helper on the RuntimeRBACTests class named extractErrorMessage(error: unknown): string that implements the current multi-source extraction (cause?.shortMessage ?? cause?.message ?? shortMessage ?? message ?? '').toString() safely, then replace the inline extraction expressions in the catch blocks for Step 2, Step 4, Step 5 (use existing), Step 7 and Step 8 with calls to this.extractErrorMessage(stepXError) and update the condition checks to test both /Missing or invalid parameters/i and /took too long to respond/i so timeout handling is consistent across all steps.scripts/check-remote-ganache-health.ts (2)
122-139: Consider parallelizing the contract reads.The three
readContractcalls are independent and could execute concurrently for faster health checks.⚡ Parallel reads with Promise.all
try { - const owner = await client.readContract({ - address: accountBloxAddress, - abi: accountBloxAbi, - functionName: 'owner', - }); - - const broadcasters = await client.readContract({ - address: accountBloxAddress, - abi: accountBloxAbi, - functionName: 'getBroadcasters', - }); - - const recovery = await client.readContract({ - address: accountBloxAddress, - abi: accountBloxAbi, - functionName: 'getRecovery', - }); + const [owner, broadcasters, recovery] = await Promise.all([ + client.readContract({ + address: accountBloxAddress, + abi: accountBloxAbi, + functionName: 'owner', + }), + client.readContract({ + address: accountBloxAddress, + abi: accountBloxAbi, + functionName: 'getBroadcasters', + }), + client.readContract({ + address: accountBloxAddress, + abi: accountBloxAbi, + functionName: 'getRecovery', + }), + ]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check-remote-ganache-health.ts` around lines 122 - 139, The three independent client.readContract calls (for functionName 'owner', 'getBroadcasters', and 'getRecovery' on accountBloxAddress using accountBloxAbi) should be run in parallel rather than sequentially; change the code to call Promise.all with the three client.readContract promises and destructure the results into owner, broadcasters, and recovery, preserving types/await usage and error handling around the combined Promise so the health check behavior remains the same.
66-86: Consider validating address format before use.The
as Addresstype assertions on lines 73 and 84 don't validate the actual address format. If the JSON or env var contains an invalid value, the error will surface later during contract calls with a less clear message.🔧 Suggested improvement using Viem's isAddress
-import { createPublicClient, http, Hex, Address } from 'viem'; +import { createPublicClient, http, Hex, Address, isAddress } from 'viem';Then validate before assignment:
if (dev && dev.AccountBlox?.address) { - accountBloxAddress = dev.AccountBlox.address as Address; + const addr = dev.AccountBlox.address; + if (isAddress(addr)) { + accountBloxAddress = addr; + } else { + console.warn(`⚠️ Invalid address in deployed-addresses.json: ${addr}`); + }Similarly for the env fallback:
if (!accountBloxAddress && process.env.ACCOUNTBLOX_ADDRESS) { - accountBloxAddress = process.env.ACCOUNTBLOX_ADDRESS as Address; + const addr = process.env.ACCOUNTBLOX_ADDRESS; + if (isAddress(addr)) { + accountBloxAddress = addr; + } else { + console.warn(`⚠️ Invalid ACCOUNTBLOX_ADDRESS env var: ${addr}`); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check-remote-ganache-health.ts` around lines 66 - 86, The code currently casts values to Address without validating format; update the logic around accountBloxAddress assignment to validate parsed JSON value and process.env.ACCOUNTBLOX_ADDRESS using a proper address checker (e.g., viem's isAddress) before assigning to accountBloxAddress: when reading deployed.development.AccountBlox.address, call isAddress and only set accountBloxAddress if valid (otherwise log a warning/error), and do the same for the fallback from process.env.ACCOUNTBLOX_ADDRESS; keep the same console messages but ensure invalid values are rejected rather than blindly cast.sdk/typescript/utils/metaTx/metaTransaction.tsx (1)
65-66: Don't erase the typed-data boundary withany.
buildTypedDataMessage()now returnsRecord<string, unknown>, and Line 210 forcessignTypedDatathroughas any. That removes compile-time checking exactly where domain/type drift would hurt most. Please give the EIP-712 payload a concrete type and let viem type-check the call end-to-end.As per coding guidelines,
**/*.{ts,tsx}: Expose a type-safe TypeScript SDK (interfaces and classes) for contract interaction (e.g., Viem clients).Also applies to: 204-210
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/typescript/utils/metaTx/metaTransaction.tsx` around lines 65 - 66, The function buildTypedDataMessage currently returns Record<string, unknown> and forces signTypedData through an as any cast around lines ~204-210, erasing compile-time EIP-712 checks; define concrete EIP-712 types (e.g., EIP712Domain, MetaTransactionTypes and the MetaTransaction message shape) and update buildTypedDataMessage(metaTx: MetaTransaction) to return the precise typed payload (the TypedData type used by viem or equivalent generic: domain, types, and message generics) so the signTypedData call can accept it without any casts; remove the as any cast where signTypedData is invoked so viem performs end-to-end type checking between buildTypedDataMessage, the domain/types constants, and the signTypedData call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/dependabot.yml:
- Line 6: Dependabot only targets the root manifest; fix by either (A) adding
Yarn/NPM workspaces to the root package.json (include the workspaces array
listing the subpackages) so Dependabot will pick up workspace manifests
automatically, then keep a single updates entry in .github/dependabot.yml
pointing at the root, or (B) add separate updates entries in
.github/dependabot.yml for each subpackage manifest (create an updates block per
package with package-ecosystem: "npm" and directory set to that subpackage) to
ensure docgen, package, and sdk/typescript manifests are updated; update the
config accordingly.
In `@scripts/sanity-sdk/guard-controller/base-test.ts`:
- Around line 643-649: The SDK isn't forwarding the explicit gas override so
eth_estimateGas still runs; update the write path in executeWriteContract
(BaseStateMachine.tsx) to extract the gas field from the passed options (in
addition to from, simulationMode, gasPrice) and include that gas value when
calling viem's writeContract; ensure callers like
roleConfigBatchRequestAndApprove (and getTxOptions) can pass a gas bigint
(1_500_000n) and it is propagated through executeWriteContract into the
writeContract invocation so the explicit gas limit prevents eth_estimateGas.
In `@scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts`:
- Around line 37-40: Change the untyped contract parameter and casts in
getOrCreateOwnershipTransferTxId to use the SecureOwnable type: replace the
parameter secureOwnableRecovery: any with secureOwnableRecovery: SecureOwnable
(the same SecureOwnable returned by createSecureOwnableWithWallet), and remove
(tx as any) and (receipt as any) casts—use the typed async call results and
their typed properties/methods (e.g., the typed transaction result fields and
receipt.status) exposed by SecureOwnable instead of unsafe casts; apply the same
typing fixes to other occurrences noted around lines 43–49 and 79–80.
- Around line 87-93: The code assumes the newly created transaction is the last
element of pendingAfter; instead, call
secureOwnableRecovery.getPendingTransactions() and filter for transactions with
type OWNERSHIP_TRANSFER and requester equal to the recovery wallet (same
filtering used earlier) after invoking transferOwnershipRequest, then locate the
matching transaction and return its ID; update the block that currently picks
pendingAfter[pendingAfter.length - 1] to search for the transaction by comparing
its type and requester (or other unique fields) and throw an error if no
matching OWNERSHIP_TRANSFER from the recovery wallet is found so the signing
flow never picks an unrelated pending transaction.
- Line 136: The assertion using boolean coercion on txId is wrong for bigint and
should be fixed: locate the call to this.assertTest(!!txId, 'Pending transaction
found for meta-tx signing') (the txId returned by
getOrCreateOwnershipTransferTxId) and either remove the redundant assertion
entirely (since getOrCreateOwnershipTransferTxId throws on error and returns a
bigint) or replace it with an explicit bigint check like this.assertTest(txId
!== 0n, 'Pending transaction found for meta-tx signing') if a zero value is
considered invalid; update only the assertion line so callers and types remain
consistent.
In `@scripts/sanity/guard-controller/base-test.cjs`:
- Around line 575-586: The timeout created for Promise.race in
sendTransactionWithValue is never cleared, which can keep the process alive;
modify sendTransactionWithValue to store the timer id when calling setTimeout
(e.g., let timer = setTimeout(...)) and ensure you clear it with
clearTimeout(timer) after the race completes (on both success and error paths) —
for example by awaiting Promise.race([sendPromise, timeoutPromise]) inside a
try/finally and calling clearTimeout(timer) in the finally block so the timer is
always removed.
In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs`:
- Around line 331-355: The ensureExactPermission function currently only
compares action bitmaps and repairs via addFunctionToRole, which reuses the
default self-referential handler mapping and can leave or reintroduce stale
handlerForSelectors, causing HandlerForSelectorMismatch; update
ensureExactPermission to also fetch and compare the handlerForSelectors for the
target function selector (using the same source as createFunctionPermission
creates), and when current handler differs from the desired handler call the
appropriate update (either pass the correct handler mapping into
addFunctionToRole or explicitly call the contract method that sets
handlerForSelector), ensuring you still call removeFunctionFromRole before
re-adding when repairing both bitmap and handler mismatches; reference
ensureExactPermission, addFunctionToRole, removeFunctionFromRole,
createFunctionPermission, and HandlerForSelectorMismatch when making the change.
In `@scripts/sanity/runtime-rbac/base-test.cjs`:
- Around line 479-494: The sendTransaction function leaves the setTimeout timer
alive because timeoutPromise is created with setTimeout that is never cleared;
update sendTransaction (the method using sendPromise, timeoutPromise) to capture
the timer id (e.g., let timer = setTimeout(...)) and ensure you
clearTimeout(timer) once Promise.race settles (both on success and on error) —
for example, create the timeout promise using the timer variable and then call
clearTimeout(timer) in a finally block or immediately after awaiting
Promise.race so no timers remain after method.send() resolves or rejects.
In `@scripts/sanity/secure-ownable/base-test.cjs`:
- Around line 310-329: The timeout created for the transaction receipt
(timeoutId from the sendTimeout Promise used with sendPromise returned by
method.send and receiptTimeoutMs) is not cleared on the error path; update the
try/catch so that if method.send() rejects you call clearTimeout(timeoutId)
inside the catch before building/throwing the new Error, ensuring the pending
timer is canceled even when sendPromise rejects.
In `@scripts/sanity/secure-ownable/eip712-signing-tests.cjs`:
- Around line 50-88: The test is only signing a synthetic hash and
manually-constructed txRecord so it doesn't exercise the contract EIP-712 path;
replace the manual construction and direct web3.eth.accounts.sign usage with the
contract-driven flow by calling generateUnsignedMetaTransactionForExisting(...)
to obtain the populated metaTx (so metaTx.message is set by the contract), then
pass that unsigned meta-transaction to eip712Signer.signMetaTransaction(...) to
produce the signature; ensure the test verifies the signature against the
contract-expected domain/types (META_TX_DOMAIN, META_TX_TYPES) and the selective
MetaTxRecord encoding (txId, params, payment) rather than a raw 0x.. message so
regressions in domain/type hashes or encoding are caught.
In `@scripts/sanity/utils/eip712-signing.cjs`:
- Around line 116-126: The generateMessageHash function currently accepts an
all-zero sentinel because _normalizeMessageHex treats it as valid; update
generateMessageHash (used by createSignedMetaTransaction) to detect and reject
the zero-placeholder by validating metaTx.message is not the zero hash (e.g.,
compare against the 32-byte zero hex like 0x000...000 or a regex /^0x0+$/ with
expected length) and throw a clear Error if detected so callers must supply the
contract-produced unsigned meta-transaction instead of signing the zero
placeholder.
---
Outside diff comments:
In `@scripts/sanity/guard-controller/base-test.cjs`:
- Around line 1853-1908: The helper ensureNativeTransferSchemaAndPermissions
currently only checks presence of schema/permission bits, which can leave
pre-split permission entries or incompatible bitmaps in place and cause
ResourceAlreadyExists in addFunctionToRole; update
ensureNativeTransferSchemaAndPermissions to normalize state for nativeSelector
by (1) when a role has any permission entry for nativeSelector, inspect the
existing bitmap and if it doesn't exactly match the desired signActions (for
ownerRoleHash) or execActions (for broadcasterRoleHash) remove or update that
function entry first (e.g., call the role/function removal or update helper
before adding) so the role ends up with exactly the expected bits, (2) handle
ResourceAlreadyExists from addFunctionToRole by attempting an idempotent
reconciliation (remove+re-add or update) rather than treating it as success, and
(3) use the existing helpers functionSchemaExists, registerFunction,
roleHasPermissionForSelector, addFunctionToRole when locating and normalizing
entries for nativeSelector to ensure the schema and both roles are in the
correct split sign/execute state.
- Around line 549-568: The code currently awaits
this.web3.eth.sendSignedTransaction(...) which can block until mining; instead,
trigger the transaction send and capture the transactionHash immediately by
using the PromiEvent events (e.g., call
this.web3.eth.sendSignedTransaction(signed.rawTransaction).once('transactionHash',
txHash => { ... }) or .on('transactionHash', ...)) rather than awaiting the
final promise—so in sendTransactionReceiptOnly replace the await on
sendSignedTransaction with subscribing to the 'transactionHash' event to obtain
transactionHash quickly (use signed.rawTransaction and store transactionHash),
then run the existing polling loop using receiptTimeoutMs to wait for the
receipt; also handle the sendSignedTransaction error event to surface send
errors.
In `@scripts/sanity/utils/eip712-signing.cjs`:
- Around line 91-106: The _normalizeMessageHex() helper is currently sanitizing,
truncating and padding inputs which can silently convert malformed digests into
different 32-byte values; change it to be strict: only accept a string that
already matches /^0x[0-9a-fA-F]{64}$/ and return null for everything else (do
not coerce bigint/other types, do not pad or truncate), and apply the same
strict behavior to the equivalent logic at lines ~181-189; ensure callers
generateMessageHash() and _signRawDigest() rely on this strict validation so
malformed digests are rejected instead of being rewritten and signed.
---
Nitpick comments:
In `@scripts/check-remote-ganache-health.ts`:
- Around line 122-139: The three independent client.readContract calls (for
functionName 'owner', 'getBroadcasters', and 'getRecovery' on accountBloxAddress
using accountBloxAbi) should be run in parallel rather than sequentially; change
the code to call Promise.all with the three client.readContract promises and
destructure the results into owner, broadcasters, and recovery, preserving
types/await usage and error handling around the combined Promise so the health
check behavior remains the same.
- Around line 66-86: The code currently casts values to Address without
validating format; update the logic around accountBloxAddress assignment to
validate parsed JSON value and process.env.ACCOUNTBLOX_ADDRESS using a proper
address checker (e.g., viem's isAddress) before assigning to accountBloxAddress:
when reading deployed.development.AccountBlox.address, call isAddress and only
set accountBloxAddress if valid (otherwise log a warning/error), and do the same
for the fallback from process.env.ACCOUNTBLOX_ADDRESS; keep the same console
messages but ensure invalid values are rejected rather than blindly cast.
In `@scripts/sanity-sdk/runtime-rbac/rbac-tests.ts`:
- Around line 695-713: Summary: duplicate error-message extraction across Steps
2,4,5,7,8 should be centralized and timeout handling normalized. Add a private
helper on the RuntimeRBACTests class named extractErrorMessage(error: unknown):
string that implements the current multi-source extraction (cause?.shortMessage
?? cause?.message ?? shortMessage ?? message ?? '').toString() safely, then
replace the inline extraction expressions in the catch blocks for Step 2, Step
4, Step 5 (use existing), Step 7 and Step 8 with calls to
this.extractErrorMessage(stepXError) and update the condition checks to test
both /Missing or invalid parameters/i and /took too long to respond/i so timeout
handling is consistent across all steps.
In `@sdk/typescript/utils/metaTx/metaTransaction.tsx`:
- Around line 65-66: The function buildTypedDataMessage currently returns
Record<string, unknown> and forces signTypedData through an as any cast around
lines ~204-210, erasing compile-time EIP-712 checks; define concrete EIP-712
types (e.g., EIP712Domain, MetaTransactionTypes and the MetaTransaction message
shape) and update buildTypedDataMessage(metaTx: MetaTransaction) to return the
precise typed payload (the TypedData type used by viem or equivalent generic:
domain, types, and message generics) so the signTypedData call can accept it
without any casts; remove the as any cast where signTypedData is invoked so viem
performs end-to-end type checking between buildTypedDataMessage, the
domain/types constants, and the signTypedData call.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b607b8fb-508e-495b-ac63-6969b032baa7
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (23)
.github/dependabot.yml.github/workflows/sync-contract-versions.ymlpackage.jsonscripts/check-remote-ganache-health.tsscripts/sanity-sdk/guard-controller/base-test.tsscripts/sanity-sdk/guard-controller/erc20-mint-controller-tests.tsscripts/sanity-sdk/guard-controller/whitelist-tests.tsscripts/sanity-sdk/runtime-rbac/rbac-tests.tsscripts/sanity-sdk/secure-ownable/eip712-signing-tests.tsscripts/sanity/guard-controller/base-test.cjsscripts/sanity/guard-controller/erc20-mint-controller-tests.cjsscripts/sanity/guard-controller/guard-controller-tests.cjsscripts/sanity/guard-controller/run-tests.cjsscripts/sanity/runtime-rbac/base-test.cjsscripts/sanity/runtime-rbac/run-tests.cjsscripts/sanity/secure-ownable/base-test.cjsscripts/sanity/secure-ownable/eip712-signing-tests.cjsscripts/sanity/secure-ownable/recovery-update-tests.cjsscripts/sanity/secure-ownable/run-tests.cjsscripts/sanity/secure-ownable/timelock-period-tests.cjsscripts/sanity/utils/eip712-signing.cjssdk/typescript/utils/metaTx/metaTransaction.tsxtest/foundry/helpers/TestHelpers.sol
🚧 Files skipped from review as they are similar to previous changes (1)
- package.json
| private async getOrCreateOwnershipTransferTxId( | ||
| secureOwnableRecovery: any, | ||
| recoveryWallet: TestWallet | ||
| ): Promise<bigint> { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify current untyped usage in this test
rg -n -C2 'secureOwnableRecovery:\s*any|\(tx as any\)|\(receipt as any\)' scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts
# Locate typed client definitions that can replace `any`
rg -n -C3 'createSecureOwnableWithWallet|getPendingTransactions|getTransaction|transferOwnershipRequest|interface TestWallet|type TestWallet' scripts sdkRepository: PracticalParticle/Bloxchain-Protocol
Length of output: 50395
🏁 Script executed:
fd -t f -e ts -e tsx 'SecureOwnable' scripts/sanity-sdk | head -20Repository: PracticalParticle/Bloxchain-Protocol
Length of output: 62
🏁 Script executed:
rg -n 'class SecureOwnable|interface SecureOwnable|type SecureOwnable|export.*SecureOwnable' -A5 sdk scriptsRepository: PracticalParticle/Bloxchain-Protocol
Length of output: 8566
🏁 Script executed:
head -30 scripts/sanity-sdk/secure-ownable/eip712-signing-tests.tsRepository: PracticalParticle/Bloxchain-Protocol
Length of output: 1014
🏁 Script executed:
head -50 scripts/sanity-sdk/secure-ownable/base-test.ts | grep -E "^import|^export"Repository: PracticalParticle/Bloxchain-Protocol
Length of output: 972
Use SecureOwnable type instead of any for contract parameter and results.
The parameter secureOwnableRecovery: any and the unsafe casts (tx as any) and (receipt as any) lose compile-time type safety for async contract calls/results in a TypeScript path that requires typed methods. Since SecureOwnable is already available from the parent class and returned by createSecureOwnableWithWallet(), use it directly:
Typed parameter fix
private async getOrCreateOwnershipTransferTxId(
secureOwnableRecovery: SecureOwnable,
recoveryWallet: TestWallet
): Promise<bigint> {Then replace the unsafe property casts with proper access through the typed SecureOwnable interface methods and typed result properties instead of (tx as any).params and (receipt as any).status.
As per coding guidelines, **/*.{ts,tsx}: "Expose a type-safe TypeScript SDK ... handle async contract calls using typed methods, returning typed results".
Also applies to: 43-49, 79-80
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts` around lines 37 -
40, Change the untyped contract parameter and casts in
getOrCreateOwnershipTransferTxId to use the SecureOwnable type: replace the
parameter secureOwnableRecovery: any with secureOwnableRecovery: SecureOwnable
(the same SecureOwnable returned by createSecureOwnableWithWallet), and remove
(tx as any) and (receipt as any) casts—use the typed async call results and
their typed properties/methods (e.g., the typed transaction result fields and
receipt.status) exposed by SecureOwnable instead of unsafe casts; apply the same
typing fixes to other occurrences noted around lines 43–49 and 79–80.
| const ensureExactPermission = async (roleHash, functionSelector, requiredActions, label) => { | ||
| const perms = await this.callContractMethod(this.contract.methods.getActiveRolePermissions(roleHash)); | ||
| const normSel = functionSelector.toLowerCase(); | ||
| const found = (perms || []).find((p) => { | ||
| const sel = (p.functionSelector ?? p[0]); | ||
| return sel && String(sel).toLowerCase() === normSel; | ||
| }); | ||
| const requiredBitmap = this.createBitmapFromActions(requiredActions); | ||
| const currentBitmapRaw = found ? (found.grantedActionsBitmap ?? found[1]) : null; | ||
| const currentBitmap = currentBitmapRaw != null ? Number(currentBitmapRaw) : null; | ||
|
|
||
| if (currentBitmap === requiredBitmap) { | ||
| console.log(` ✅ ${label} already correct (bitmap=${requiredBitmap})`); | ||
| return; | ||
| } | ||
|
|
||
| if (currentBitmap != null) { | ||
| console.log(` 🔧 ${label} bitmap mismatch: current=${currentBitmap}, required=${requiredBitmap}. Replacing...`); | ||
| await this.removeFunctionFromRole(roleHash, functionSelector, ownerPrivateKey, broadcasterWallet); | ||
| } else { | ||
| console.log(` 🔧 ${label} missing. Adding...`); | ||
| } | ||
|
|
||
| // Remove existing mint/handler permissions first so we always set the correct bitmap (idempotent; ignores ResourceNotFound) | ||
| await this.removeFunctionFromRole(this.getRoleHash('MINT_REQUESTOR'), ERC20_MINT_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('MINT_REQUESTOR'), this.EXECUTE_WITH_TIMELOCK_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('MINT_APPROVER'), ERC20_MINT_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('MINT_APPROVER'), this.REQUEST_AND_APPROVE_EXECUTION_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('MINT_APPROVER'), this.APPROVE_TIMELOCK_EXECUTION_META_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('MINT_APPROVER'), this.CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('BROADCASTER_ROLE'), ERC20_MINT_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('BROADCASTER_ROLE'), this.REQUEST_AND_APPROVE_EXECUTION_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('BROADCASTER_ROLE'), this.APPROVE_TIMELOCK_EXECUTION_META_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.removeFunctionFromRole(this.getRoleHash('BROADCASTER_ROLE'), this.CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, ownerPrivateKey, broadcasterWallet); | ||
| await this.addFunctionToRole(roleHash, functionSelector, requiredActions, ownerPrivateKey, broadcasterWallet); | ||
| await new Promise(r => setTimeout(r, 500)); |
There was a problem hiding this comment.
ensureExactPermission() is still lossy for handler selectors.
For approveTimeLockExecutionWithMetaTx and cancelTimeLockExecutionWithMetaTx, this helper only compares the bitmap and repairs via addFunctionToRole(). That writes the default self-referential handlerForSelectors from BaseGuardControllerTest.createFunctionPermission(), so a stale/wrong handler mapping can pass here or be reintroduced here and later fail with HandlerForSelectorMismatch.
♻️ Suggested fix
- const ensureExactPermission = async (roleHash, functionSelector, requiredActions, label) => {
+ const ensureExactPermission = async (roleHash, functionSelector, requiredActions, label, handlerForSelectors = null) => {
const perms = await this.callContractMethod(this.contract.methods.getActiveRolePermissions(roleHash));
const normSel = functionSelector.toLowerCase();
const found = (perms || []).find((p) => {
const sel = (p.functionSelector ?? p[0]);
return sel && String(sel).toLowerCase() === normSel;
});
const requiredBitmap = this.createBitmapFromActions(requiredActions);
+ const expectedHandlers = (handlerForSelectors ?? [functionSelector])
+ .map((s) => String(s).toLowerCase())
+ .sort();
const currentBitmapRaw = found ? (found.grantedActionsBitmap ?? found[1]) : null;
const currentBitmap = currentBitmapRaw != null ? Number(currentBitmapRaw) : null;
+ const currentHandlers = found
+ ? (found.handlerForSelectors ?? found[2] ?? []).map((s) => String(s).toLowerCase()).sort()
+ : [];
- if (currentBitmap === requiredBitmap) {
+ if (currentBitmap === requiredBitmap && JSON.stringify(currentHandlers) === JSON.stringify(expectedHandlers)) {
console.log(` ✅ ${label} already correct (bitmap=${requiredBitmap})`);
return;
}
if (currentBitmap != null) {
console.log(` 🔧 ${label} bitmap mismatch: current=${currentBitmap}, required=${requiredBitmap}. Replacing...`);
await this.removeFunctionFromRole(roleHash, functionSelector, ownerPrivateKey, broadcasterWallet);
} else {
console.log(` 🔧 ${label} missing. Adding...`);
}
- await this.addFunctionToRole(roleHash, functionSelector, requiredActions, ownerPrivateKey, broadcasterWallet);
+ if (handlerForSelectors) {
+ await this.addFunctionToRoleWithHandlerForSelectors(
+ roleHash,
+ functionSelector,
+ requiredActions,
+ handlerForSelectors,
+ ownerPrivateKey,
+ broadcasterWallet
+ );
+ } else {
+ await this.addFunctionToRole(roleHash, functionSelector, requiredActions, ownerPrivateKey, broadcasterWallet);
+ }
await new Promise(r => setTimeout(r, 500));
};- await ensureExactPermission(approverRoleHash, this.APPROVE_TIMELOCK_EXECUTION_META_SELECTOR, [this.TxAction.SIGN_META_APPROVE], 'MINT_APPROVER approveTimeLockExecutionWithMetaTx sign');
- await ensureExactPermission(approverRoleHash, this.CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, [this.TxAction.SIGN_META_CANCEL], 'MINT_APPROVER cancelTimeLockExecutionWithMetaTx sign');
+ await ensureExactPermission(approverRoleHash, this.APPROVE_TIMELOCK_EXECUTION_META_SELECTOR, [this.TxAction.SIGN_META_APPROVE], 'MINT_APPROVER approveTimeLockExecutionWithMetaTx sign', [ERC20_MINT_SELECTOR]);
+ await ensureExactPermission(approverRoleHash, this.CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, [this.TxAction.SIGN_META_CANCEL], 'MINT_APPROVER cancelTimeLockExecutionWithMetaTx sign', [ERC20_MINT_SELECTOR]);
- await ensureExactPermission(broadcasterRoleHash, this.APPROVE_TIMELOCK_EXECUTION_META_SELECTOR, broadcasterApproveCancelActions, 'BROADCASTER_ROLE approveTimeLockExecutionWithMetaTx execute');
- await ensureExactPermission(broadcasterRoleHash, this.CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, broadcasterApproveCancelActions, 'BROADCASTER_ROLE cancelTimeLockExecutionWithMetaTx execute');
+ await ensureExactPermission(broadcasterRoleHash, this.APPROVE_TIMELOCK_EXECUTION_META_SELECTOR, [this.TxAction.EXECUTE_META_APPROVE], 'BROADCASTER_ROLE approveTimeLockExecutionWithMetaTx execute', [ERC20_MINT_SELECTOR]);
+ await ensureExactPermission(broadcasterRoleHash, this.CANCEL_TIMELOCK_EXECUTION_META_SELECTOR, [this.TxAction.EXECUTE_META_CANCEL], 'BROADCASTER_ROLE cancelTimeLockExecutionWithMetaTx execute', [ERC20_MINT_SELECTOR]);Also applies to: 365-372
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/sanity/guard-controller/erc20-mint-controller-tests.cjs` around lines
331 - 355, The ensureExactPermission function currently only compares action
bitmaps and repairs via addFunctionToRole, which reuses the default
self-referential handler mapping and can leave or reintroduce stale
handlerForSelectors, causing HandlerForSelectorMismatch; update
ensureExactPermission to also fetch and compare the handlerForSelectors for the
target function selector (using the same source as createFunctionPermission
creates), and when current handler differs from the desired handler call the
appropriate update (either pass the correct handler mapping into
addFunctionToRole or explicitly call the contract method that sets
handlerForSelector), ensuring you still call removeFunctionFromRole before
re-adding when repairing both bitmap and handler mismatches; reference
ensureExactPermission, addFunctionToRole, removeFunctionFromRole,
createFunctionPermission, and HandlerForSelectorMismatch when making the change.
This commit updates the dependabot configuration to include additional package ecosystems for documentation generation utilities, published contract packages, and TypeScript SDK packages. Each new entry is scheduled for weekly updates to the development branch, ensuring that dependencies remain current and reducing potential security vulnerabilities. This enhancement streamlines the dependency management process across various project components.
This commit enhances the gas limit handling in the BaseStateMachine class by adding validation for the gas option provided by the caller. It ensures that the gas limit is a non-negative integer, converting it to a bigint if necessary. This change improves error handling and provides clearer feedback for invalid gas inputs, aligning with best practices for type safety in TypeScript.
This commit introduces a check to ensure that the gas limit provided in the BaseStateMachine class is non-negative. If a negative value is detected, an error is thrown with a clear message indicating the issue. This enhancement improves error handling and aligns with best practices for input validation in TypeScript, further strengthening the robustness of the gas limit handling implemented in the previous commit.
This commit updates the TransactionOptions interface to allow the gas property to accept a number, bigint, or string. Additionally, it introduces validation in the BaseStateMachine class to ensure that number inputs for gas are non-negative safe integers. This enhancement improves type safety and error handling, providing clearer feedback for invalid gas inputs and aligning with best practices in TypeScript.
chore: enhance dependabot configuration for improved package management
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
scripts/sanity/guard-controller/base-test.cjs (1)
580-590:⚠️ Potential issue | 🟠 MajorTimeout cleanup still misses the rejection path in
sendTransactionWithValue.Line 590 clears the timer only after a successful
Promise.raceresolution. IfsendPromiserejects first, control jumps tocatchand the timer remains active until it fires.♻️ Proposed fix
- const result = await Promise.race([sendPromise, timeoutPromise]); - if (timeoutId) clearTimeout(timeoutId); + let result; + try { + result = await Promise.race([sendPromise, timeoutPromise]); + } finally { + if (timeoutId) clearTimeout(timeoutId); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/guard-controller/base-test.cjs` around lines 580 - 590, The timeout timer created in sendTransactionWithValue can remain active if sendPromise rejects because clearTimeout(timeoutId) is only called after a successful await; change the logic to always clear the timer regardless of how Promise.race resolves by moving clearTimeout(timeoutId) into a finally block (or add a .finally handler) around the await of Promise.race([sendPromise, timeoutPromise]) so timeoutId is cleared on both resolution and rejection.scripts/sanity/utils/eip712-signing.cjs (1)
116-123:⚠️ Potential issue | 🟠 Major
generateMessageHashnow conflicts withcreateSignedMetaTransactiondefaults.With the zero-sentinel rejection added here, the path seeded at Line 249 (
message: 0x00..00) now always fails whencreateSignedMetaTransaction()callssignMetaTransaction(). Please thread a contract-generated unsigned meta-tx (with populatedmessage) into this flow instead of constructing a zero-placeholder message.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/utils/eip712-signing.cjs` around lines 116 - 123, The new zero-sentinel rejection in generateMessageHash causes createSignedMetaTransaction -> signMetaTransaction to fail when callers pass a zeroed message; instead of constructing a zero-placeholder message, change the flow so createSignedMetaTransaction/signMetaTransaction receive the contract-generated unsigned meta-tx that already has a populated message (use generateUnsignedMetaTransactionForNew or generateUnsignedMetaTransactionForExisting to obtain the populated metaTx and thread that object through). Update any call sites of createSignedMetaTransaction/signMetaTransaction to accept and forward the unsigned meta-tx returned by the contract generator and remove/avoid creating a '0x00...00' message placeholder so generateMessageHash can validate correctly.scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts (1)
37-53:⚠️ Potential issue | 🟠 MajorRemove
anyin ownership-transfer tx lookup and use typedSecureOwnableresults.Using
any+ positional fallbacks (params?.[4],params?.[0]) drops compile-time guarantees and makes this helper brittle against SDK shape changes.🔧 Typed alternative
+import { SecureOwnable } from '../../../sdk/typescript/contracts/core/SecureOwnable.tsx'; ... - private async getOrCreateOwnershipTransferTxId( - secureOwnableRecovery: any, + private async getOrCreateOwnershipTransferTxId( + secureOwnableRecovery: SecureOwnable, recoveryWallet: TestWallet ): Promise<bigint> { ... - for (const id of pendingTxs as bigint[]) { + for (const id of pendingTxs) { const tx = await secureOwnableRecovery.getTransaction(id); - const params = (tx as any).params ?? (tx as any)[3]; - const op = (params?.operationType ?? params?.[4]) as string | undefined; - const requester = (params?.requester ?? params?.[0]) as string | undefined; + const op = tx.params.operationType.toLowerCase(); + const requester = tx.params.requester.toLowerCase(); - const isOwnershipTransfer = - !!op && op.toLowerCase() === ownershipOp; - const isFromRecovery = - !!requester && requester.toLowerCase() === recoveryAddr; + const isOwnershipTransfer = op === ownershipOp; + const isFromRecovery = requester === recoveryAddr; ... - const status = (receipt as any).status; - const ok = status === 'success' || status === 1 || String(status) === '1'; + const ok = receipt.status === 'success';#!/bin/bash # Verify untyped access patterns still present in this helper. rg -n -C2 'secureOwnableRecovery:\s*any|\(tx as any\)|\(receipt as any\)|params\?\.\[[0-9]+\]' \ scripts/sanity-sdk/secure-ownable/eip712-signing-tests.tsAs per coding guidelines,
**/*.{ts,tsx}: "Expose a type-safe TypeScript SDK ... handle async contract calls using typed methods, returning typed results".Also applies to: 87-88
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts` around lines 37 - 53, The helper getOrCreateOwnershipTransferTxId is using untyped any and positional fallbacks (secureOwnableRecovery: any, (tx as any), params?.[4], params?.[0]) which is brittle; replace any with the correct SecureOwnable SDK types and use the typed return shapes from secureOwnableRecovery.getPendingTransactions() and getTransaction() (define or import an interface for the transaction/result shape), then read named properties (e.g., params.operationType and params.requester) instead of numeric indexes; update the function signature and local variables to use those types so the op and requester extraction no longer needs casting or positional fallbacks (also apply the same typed fixes at the other occurrences around lines 87-88).
🧹 Nitpick comments (6)
sdk/typescript/interfaces/base.index.tsx (1)
17-17: Clarify the acceptedgasstring format in the interface.
gasnow allowsstring, but runtime validation insdk/typescript/contracts/core/BaseStateMachine.tsx(Line 106-Line 110) accepts only non-negative decimal digits. Add a short field doc here (or narrow the type alias) so callers don’t pass unsupported formats like hex strings and hit runtime errors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/typescript/interfaces/base.index.tsx` at line 17, The gas property currently accepts string but runtime validation in BaseStateMachine.tsx only allows non-negative decimal digits; update the gas declaration (gas?: number | bigint | string) to either narrow the type or, more simply, add a short JSDoc on the gas field clarifying the accepted string format (e.g., "string must be a non-negative decimal integer, digits only, no hex or sign") and reference the runtime validator in BaseStateMachine so callers don't pass unsupported formats.scripts/check-remote-ganache-health.ts (2)
42-56: UseREMOTE_NETWORK_IDas a real assertion, not just a log line.The script already fetches the remote node identity, but the configured expected value is informational only. If this check is meant to catch a mispointed RPC endpoint, it should fail when the connected node is not the intended one.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check-remote-ganache-health.ts` around lines 42 - 56, The script currently only logs process.env.REMOTE_NETWORK_ID instead of enforcing it; add a real assertion that compares Number(process.env.REMOTE_NETWORK_ID) to the fetched chainId (the chainId value returned by client.getChainId()) and fail fast if they differ. Locate the block using process.env.REMOTE_NETWORK_ID and replace the logging-only behavior with a check that computes networkId = Number(process.env.REMOTE_NETWORK_ID), compares networkId !== chainId, logs an explicit error including both values via console.error or processLogger, and exits non-zero (e.g., process.exit(1)) so the script fails when the RPC endpoint is not the expected network. Ensure you still log the env value when it matches for visibility.
98-120: Avoid maintaining a handwritten ABI copy in this script.This PR already updates ABI artifacts elsewhere. Keeping a partial inline ABI creates another place to drift when
AccountBloxsignatures change, which can break the health check independently of the shared ABI/SDK surface.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check-remote-ganache-health.ts` around lines 98 - 120, Replace the handwritten accountBloxAbi constant with the canonical ABI from your build/artifact or typechain output: remove the inline accountBloxAbi array and import the ABI (or use AccountBlox__factory.abi) from the generated artifact/typechain so the script always uses the shared AccountBlox ABI; update any code that referenced accountBloxAbi to use the imported ABI symbol instead.scripts/sanity/guard-controller/base-test.cjs (1)
923-966: Extract shared unsigned-meta decode/normalization logic to avoid drift.Line 940 through Line 965 duplicates the normalization path already used in
_callGenerateUnsignedMetaTransactionForNewRaw. A single helper would reduce maintenance risk and keep both code paths behavior-identical.♻️ Refactor sketch
+ _formatUnsignedMetaTxDecoded(decoded) { + const single = decoded.__length__ === 1; + const result = single ? decoded[0] : decoded; + const tuple = result && (result.txRecord !== undefined || result[0] !== undefined) ? result : (single ? decoded : result); + const msg = (tuple && (tuple.message ?? tuple[2])) ?? (result && (result.message ?? result[2])); + const sig = (tuple && (tuple.signature ?? tuple[3])) ?? (result && (result.signature ?? result[3])); + const dat = (tuple && (tuple.data ?? tuple[4])) ?? (result && (result.data ?? result[4])); + const txRecord = (tuple && (tuple.txRecord ?? tuple[0])) ?? (result && (result.txRecord ?? result[0])); + const params = (tuple && (tuple.params ?? tuple[1])) ?? (result && (result.params ?? result[1])); + let messageHex = msg; + if (msg != null) { + const raw = typeof msg === 'string' ? msg : this.web3.utils.toHex(msg); + messageHex = raw.startsWith('0x') ? raw : '0x' + raw; + if (messageHex.length < 66) messageHex = '0x' + messageHex.slice(2).padStart(64, '0'); + } + if (txRecord != null && messageHex != null && typeof txRecord === 'object' && !Array.isArray(txRecord)) { + txRecord.message = txRecord.message ?? messageHex; + } + return { txRecord, params, message: messageHex, signature: sig !== undefined ? sig : '0x', data: dat }; + } - const single = decoded.__length__ === 1; - const result = single ? decoded[0] : decoded; - ... - return { txRecord, params, message: messageHex, signature: sig !== undefined ? sig : '0x', data: dat }; + return this._formatUnsignedMetaTxDecoded(decoded);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/guard-controller/base-test.cjs` around lines 923 - 966, The duplicated normalization/decoding logic in _callGenerateUnsignedMetaTransactionForExistingRaw (lines handling tuple/msg/sig/dat/txRecord/params/messageHex and txRecord.message mutation) should be extracted into a single helper (e.g., _normalizeUnsignedMetaOutput) and used by both _callGenerateUnsignedMetaTransactionForExistingRaw and _callGenerateUnsignedMetaTransactionForNewRaw; move the code that takes decoded (or result) and derives tuple, msg, sig, dat, txRecord, params, computes messageHex (padding/0x) and sets txRecord.message when appropriate into that helper, have the helper return { txRecord, params, message, signature, data }, and replace the duplicated block in both functions with a call to the new helper to ensure identical behavior and reduce drift.scripts/sanity-sdk/secure-ownable/broadcaster-update-tests.ts (1)
95-100: Consider hoisting the repeated gas literal into a shared constant.
500_000nis repeated in four places in this file; extracting a local constant will reduce drift and make future tuning easier.♻️ Suggested cleanup
+const BROADCASTER_REQUEST_GAS = 500_000n; ... - this.getTxOptions(ownerWallet.address, { gas: 500_000n }) + this.getTxOptions(ownerWallet.address, { gas: BROADCASTER_REQUEST_GAS })Also applies to: 167-171, 220-224, 307-311
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity-sdk/secure-ownable/broadcaster-update-tests.ts` around lines 95 - 100, Hoist the repeated numeric gas literal into a single shared constant (e.g., DEFAULT_GAS = 500_000n) at the top of the test file and replace each inline use of 500_000n in the getTxOptions calls (examples: this.getTxOptions(ownerWallet.address, { gas: 500_000n }) used with secureOwnableOwner.updateBroadcasterRequest and the other three occurrences) with that constant so future tuning updates only touch one definition.scripts/sanity/secure-ownable/base-test.cjs (1)
296-301: ValidateoperationTypeinstead of silently rewriting it.Stripping non-hex characters and taking the last 64 nibbles can turn malformed input into a different hash, which hides decode/ABI mismatches and can misroute
getOperationName()/cancelTransaction().Suggested hardening
normalizeOperationType(operationType) { if (operationType == null) return null; - let hex = typeof operationType === 'string' ? operationType : this.web3.utils.toHex(operationType); - if (!hex || !hex.startsWith('0x')) hex = '0x' + hex; - const body = hex.slice(2).replace(/[^0-9a-fA-F]/g, ''); - return '0x' + (body.length >= 64 ? body.slice(-64) : body.padStart(64, '0')).toLowerCase(); + const raw = typeof operationType === 'string' + ? operationType + : this.web3.utils.toHex(operationType); + const hex = raw.startsWith('0x') ? raw : `0x${raw}`; + if (!/^0x[0-9a-fA-F]{1,64}$/.test(hex)) { + throw new Error(`Unexpected operationType format: ${operationType}`); + } + return `0x${hex.slice(2).padStart(64, '0').toLowerCase()}`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/sanity/secure-ownable/base-test.cjs` around lines 296 - 301, normalizeOperationType currently sanitizes and reshapes malformed input which can hide ABI/decode mismatches; instead validate strictly in normalizeOperationType: if operationType is null return null, else convert non-string using this.web3.utils.toHex(operationType) only once, then assert the result matches the exact bytes32 hex pattern /^0x[0-9a-fA-F]{64}$/ and return it lowercased; if it does not match, throw a clear TypeError describing the invalid operationType (referenced by normalizeOperationType) so callers like getOperationName and cancelTransaction fail fast rather than silently rewriting inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/check-remote-ganache-health.ts`:
- Around line 145-149: The catch block currently logs "AccountBlox read failed
(owner/getBroadcasters/getRecovery reverted)" which incorrectly assumes a
revert; change the headline to a neutral message like "AccountBlox read failed"
(or "AccountBlox read failed (call error)") and include diagnostic details
(err.name, err.code, err?.message, and the existing data extraction) so callers
can distinguish revert vs bad address/ABI/RPC issues; keep the existing
extraction into the data variable (`const data: \`0x\${string}\` | undefined =
err?.data ?? err?.cause?.data ?? err?.cause?.cause?.data;`) and log it alongside
the error type/message.
- Around line 68-86: The code currently reads deployed-addresses.json first and
sets accountBloxAddress from deployed.development, which prevents the explicit
environment override from process.env.ACCOUNTBLOX_ADDRESS; change the lookup
order so the env var wins: first check process.env.ACCOUNTBLOX_ADDRESS and set
accountBloxAddress from it (logging the env source) before attempting to read
deployedPath/parse deployed.development, or if you prefer, after reading the
file explicitly prefer process.env.ACCOUNTBLOX_ADDRESS to override any value set
from the file; refer to the symbols accountBloxAddress, deployedPath,
deployed.development and process.env.ACCOUNTBLOX_ADDRESS when making the change.
In `@scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts`:
- Line 7: The import in eip712-signing-tests.ts imports TestWallet from the
wrong module; change the import so TestWallet is imported from the module that
actually exports it (the BaseSDKTest module) instead of './base-test.ts' —
update the import that currently reads "import { BaseSecureOwnableTest,
TestWallet } from './base-test.ts';" to import BaseSecureOwnableTest and
TestWallet from the module that defines BaseSDKTest/TestWallet.
In `@scripts/sanity/secure-ownable/base-test.cjs`:
- Around line 219-223: The test setup currently continues when
this.clearPendingTransactions() returns false, which masks real cleanup
failures; change the behavior to fail fast by treating a false return as an
error — in the same initialization/context where clearPendingTransactions is
called, replace the console.log fallback with throwing an Error (or calling
process.exit(1)) including a descriptive message like "Failed to clear pending
transactions: cannot guarantee clean on-chain state" so the suite aborts
immediately when this.clearPendingTransactions() returns false.
---
Duplicate comments:
In `@scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts`:
- Around line 37-53: The helper getOrCreateOwnershipTransferTxId is using
untyped any and positional fallbacks (secureOwnableRecovery: any, (tx as any),
params?.[4], params?.[0]) which is brittle; replace any with the correct
SecureOwnable SDK types and use the typed return shapes from
secureOwnableRecovery.getPendingTransactions() and getTransaction() (define or
import an interface for the transaction/result shape), then read named
properties (e.g., params.operationType and params.requester) instead of numeric
indexes; update the function signature and local variables to use those types so
the op and requester extraction no longer needs casting or positional fallbacks
(also apply the same typed fixes at the other occurrences around lines 87-88).
In `@scripts/sanity/guard-controller/base-test.cjs`:
- Around line 580-590: The timeout timer created in sendTransactionWithValue can
remain active if sendPromise rejects because clearTimeout(timeoutId) is only
called after a successful await; change the logic to always clear the timer
regardless of how Promise.race resolves by moving clearTimeout(timeoutId) into a
finally block (or add a .finally handler) around the await of
Promise.race([sendPromise, timeoutPromise]) so timeoutId is cleared on both
resolution and rejection.
In `@scripts/sanity/utils/eip712-signing.cjs`:
- Around line 116-123: The new zero-sentinel rejection in generateMessageHash
causes createSignedMetaTransaction -> signMetaTransaction to fail when callers
pass a zeroed message; instead of constructing a zero-placeholder message,
change the flow so createSignedMetaTransaction/signMetaTransaction receive the
contract-generated unsigned meta-tx that already has a populated message (use
generateUnsignedMetaTransactionForNew or
generateUnsignedMetaTransactionForExisting to obtain the populated metaTx and
thread that object through). Update any call sites of
createSignedMetaTransaction/signMetaTransaction to accept and forward the
unsigned meta-tx returned by the contract generator and remove/avoid creating a
'0x00...00' message placeholder so generateMessageHash can validate correctly.
---
Nitpick comments:
In `@scripts/check-remote-ganache-health.ts`:
- Around line 42-56: The script currently only logs
process.env.REMOTE_NETWORK_ID instead of enforcing it; add a real assertion that
compares Number(process.env.REMOTE_NETWORK_ID) to the fetched chainId (the
chainId value returned by client.getChainId()) and fail fast if they differ.
Locate the block using process.env.REMOTE_NETWORK_ID and replace the
logging-only behavior with a check that computes networkId =
Number(process.env.REMOTE_NETWORK_ID), compares networkId !== chainId, logs an
explicit error including both values via console.error or processLogger, and
exits non-zero (e.g., process.exit(1)) so the script fails when the RPC endpoint
is not the expected network. Ensure you still log the env value when it matches
for visibility.
- Around line 98-120: Replace the handwritten accountBloxAbi constant with the
canonical ABI from your build/artifact or typechain output: remove the inline
accountBloxAbi array and import the ABI (or use AccountBlox__factory.abi) from
the generated artifact/typechain so the script always uses the shared
AccountBlox ABI; update any code that referenced accountBloxAbi to use the
imported ABI symbol instead.
In `@scripts/sanity-sdk/secure-ownable/broadcaster-update-tests.ts`:
- Around line 95-100: Hoist the repeated numeric gas literal into a single
shared constant (e.g., DEFAULT_GAS = 500_000n) at the top of the test file and
replace each inline use of 500_000n in the getTxOptions calls (examples:
this.getTxOptions(ownerWallet.address, { gas: 500_000n }) used with
secureOwnableOwner.updateBroadcasterRequest and the other three occurrences)
with that constant so future tuning updates only touch one definition.
In `@scripts/sanity/guard-controller/base-test.cjs`:
- Around line 923-966: The duplicated normalization/decoding logic in
_callGenerateUnsignedMetaTransactionForExistingRaw (lines handling
tuple/msg/sig/dat/txRecord/params/messageHex and txRecord.message mutation)
should be extracted into a single helper (e.g., _normalizeUnsignedMetaOutput)
and used by both _callGenerateUnsignedMetaTransactionForExistingRaw and
_callGenerateUnsignedMetaTransactionForNewRaw; move the code that takes decoded
(or result) and derives tuple, msg, sig, dat, txRecord, params, computes
messageHex (padding/0x) and sets txRecord.message when appropriate into that
helper, have the helper return { txRecord, params, message, signature, data },
and replace the duplicated block in both functions with a call to the new helper
to ensure identical behavior and reduce drift.
In `@scripts/sanity/secure-ownable/base-test.cjs`:
- Around line 296-301: normalizeOperationType currently sanitizes and reshapes
malformed input which can hide ABI/decode mismatches; instead validate strictly
in normalizeOperationType: if operationType is null return null, else convert
non-string using this.web3.utils.toHex(operationType) only once, then assert the
result matches the exact bytes32 hex pattern /^0x[0-9a-fA-F]{64}$/ and return it
lowercased; if it does not match, throw a clear TypeError describing the invalid
operationType (referenced by normalizeOperationType) so callers like
getOperationName and cancelTransaction fail fast rather than silently rewriting
inputs.
In `@sdk/typescript/interfaces/base.index.tsx`:
- Line 17: The gas property currently accepts string but runtime validation in
BaseStateMachine.tsx only allows non-negative decimal digits; update the gas
declaration (gas?: number | bigint | string) to either narrow the type or, more
simply, add a short JSDoc on the gas field clarifying the accepted string format
(e.g., "string must be a non-negative decimal integer, digits only, no hex or
sign") and reference the runtime validator in BaseStateMachine so callers don't
pass unsupported formats.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ef902b26-2cf6-43bb-81ff-a3bda4252161
📒 Files selected for processing (13)
.github/dependabot.ymlscripts/check-remote-ganache-health.tsscripts/sanity-sdk/secure-ownable/base-test.tsscripts/sanity-sdk/secure-ownable/broadcaster-update-tests.tsscripts/sanity-sdk/secure-ownable/eip712-signing-tests.tsscripts/sanity-sdk/secure-ownable/meta-tx-execution-tests.tsscripts/sanity-sdk/secure-ownable/timelock-period-tests.tsscripts/sanity/guard-controller/base-test.cjsscripts/sanity/runtime-rbac/base-test.cjsscripts/sanity/secure-ownable/base-test.cjsscripts/sanity/utils/eip712-signing.cjssdk/typescript/contracts/core/BaseStateMachine.tsxsdk/typescript/interfaces/base.index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- .github/dependabot.yml
| if (fs.existsSync(deployedPath)) { | ||
| try { | ||
| const deployed = JSON.parse(fs.readFileSync(deployedPath, 'utf8')); | ||
| const dev = deployed.development; | ||
| if (dev && dev.AccountBlox?.address) { | ||
| accountBloxAddress = dev.AccountBlox.address as `0x${string}`; | ||
| console.log( | ||
| `📋 AccountBlox from deployed-addresses.json (development): ${accountBloxAddress}` | ||
| ); | ||
| } | ||
| } catch (e) { | ||
| console.warn(`⚠️ Could not read deployed-addresses.json: ${(e as Error).message}`); | ||
| } | ||
| } | ||
|
|
||
| if (!accountBloxAddress && process.env.ACCOUNTBLOX_ADDRESS) { | ||
| accountBloxAddress = process.env.ACCOUNTBLOX_ADDRESS as `0x${string}`; | ||
| console.log(`📋 AccountBlox from ACCOUNTBLOX_ADDRESS (env): ${accountBloxAddress}`); | ||
| } |
There was a problem hiding this comment.
Let ACCOUNTBLOX_ADDRESS override deployed-addresses.json.
For a remote health check, the explicit env var is the operator override. The current lookup order ignores it whenever a local deployed-addresses.json exists, so a stale development address can make the script probe the wrong contract.
🔧 Suggested change
- if (fs.existsSync(deployedPath)) {
+ if (process.env.ACCOUNTBLOX_ADDRESS) {
+ accountBloxAddress = process.env.ACCOUNTBLOX_ADDRESS as `0x${string}`;
+ console.log(`📋 AccountBlox from ACCOUNTBLOX_ADDRESS (env): ${accountBloxAddress}`);
+ } else if (fs.existsSync(deployedPath)) {
try {
const deployed = JSON.parse(fs.readFileSync(deployedPath, 'utf8'));
const dev = deployed.development;
if (dev && dev.AccountBlox?.address) {
accountBloxAddress = dev.AccountBlox.address as `0x${string}`;
@@
- if (!accountBloxAddress && process.env.ACCOUNTBLOX_ADDRESS) {
- accountBloxAddress = process.env.ACCOUNTBLOX_ADDRESS as `0x${string}`;
- console.log(`📋 AccountBlox from ACCOUNTBLOX_ADDRESS (env): ${accountBloxAddress}`);
- }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (fs.existsSync(deployedPath)) { | |
| try { | |
| const deployed = JSON.parse(fs.readFileSync(deployedPath, 'utf8')); | |
| const dev = deployed.development; | |
| if (dev && dev.AccountBlox?.address) { | |
| accountBloxAddress = dev.AccountBlox.address as `0x${string}`; | |
| console.log( | |
| `📋 AccountBlox from deployed-addresses.json (development): ${accountBloxAddress}` | |
| ); | |
| } | |
| } catch (e) { | |
| console.warn(`⚠️ Could not read deployed-addresses.json: ${(e as Error).message}`); | |
| } | |
| } | |
| if (!accountBloxAddress && process.env.ACCOUNTBLOX_ADDRESS) { | |
| accountBloxAddress = process.env.ACCOUNTBLOX_ADDRESS as `0x${string}`; | |
| console.log(`📋 AccountBlox from ACCOUNTBLOX_ADDRESS (env): ${accountBloxAddress}`); | |
| } | |
| if (process.env.ACCOUNTBLOX_ADDRESS) { | |
| accountBloxAddress = process.env.ACCOUNTBLOX_ADDRESS as `0x${string}`; | |
| console.log(`📋 AccountBlox from ACCOUNTBLOX_ADDRESS (env): ${accountBloxAddress}`); | |
| } else if (fs.existsSync(deployedPath)) { | |
| try { | |
| const deployed = JSON.parse(fs.readFileSync(deployedPath, 'utf8')); | |
| const dev = deployed.development; | |
| if (dev && dev.AccountBlox?.address) { | |
| accountBloxAddress = dev.AccountBlox.address as `0x${string}`; | |
| console.log( | |
| `📋 AccountBlox from deployed-addresses.json (development): ${accountBloxAddress}` | |
| ); | |
| } | |
| } catch (e) { | |
| console.warn(`⚠️ Could not read deployed-addresses.json: ${(e as Error).message}`); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/check-remote-ganache-health.ts` around lines 68 - 86, The code
currently reads deployed-addresses.json first and sets accountBloxAddress from
deployed.development, which prevents the explicit environment override from
process.env.ACCOUNTBLOX_ADDRESS; change the lookup order so the env var wins:
first check process.env.ACCOUNTBLOX_ADDRESS and set accountBloxAddress from it
(logging the env source) before attempting to read deployedPath/parse
deployed.development, or if you prefer, after reading the file explicitly prefer
process.env.ACCOUNTBLOX_ADDRESS to override any value set from the file; refer
to the symbols accountBloxAddress, deployedPath, deployed.development and
process.env.ACCOUNTBLOX_ADDRESS when making the change.
| } catch (err: any) { | ||
| console.error('❌ AccountBlox read failed (owner/getBroadcasters/getRecovery reverted)'); | ||
| console.error(` Message: ${err?.message || err}`); | ||
| const data: `0x${string}` | undefined = | ||
| err?.data ?? err?.cause?.data ?? err?.cause?.cause?.data; |
There was a problem hiding this comment.
Don’t label every read failure as a revert.
This catch also handles bad addresses, ABI mismatches, and RPC/transport failures. The current headline can send debugging toward the contract when the real issue is config or connectivity.
🪪 Suggested change
- console.error('❌ AccountBlox read failed (owner/getBroadcasters/getRecovery reverted)');
+ console.error('❌ AccountBlox read failed');📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (err: any) { | |
| console.error('❌ AccountBlox read failed (owner/getBroadcasters/getRecovery reverted)'); | |
| console.error(` Message: ${err?.message || err}`); | |
| const data: `0x${string}` | undefined = | |
| err?.data ?? err?.cause?.data ?? err?.cause?.cause?.data; | |
| } catch (err: any) { | |
| console.error('❌ AccountBlox read failed'); | |
| console.error(` Message: ${err?.message || err}`); | |
| const data: `0x${string}` | undefined = | |
| err?.data ?? err?.cause?.data ?? err?.cause?.cause?.data; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/check-remote-ganache-health.ts` around lines 145 - 149, The catch
block currently logs "AccountBlox read failed (owner/getBroadcasters/getRecovery
reverted)" which incorrectly assumes a revert; change the headline to a neutral
message like "AccountBlox read failed" (or "AccountBlox read failed (call
error)") and include diagnostic details (err.name, err.code, err?.message, and
the existing data extraction) so callers can distinguish revert vs bad
address/ABI/RPC issues; keep the existing extraction into the data variable
(`const data: \`0x\${string}\` | undefined = err?.data ?? err?.cause?.data ??
err?.cause?.cause?.data;`) and log it alongside the error type/message.
|
|
||
| import { Address, Hex } from 'viem'; | ||
| import { BaseSecureOwnableTest } from './base-test.ts'; | ||
| import { BaseSecureOwnableTest, TestWallet } from './base-test.ts'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify where TestWallet is exported/imported.
rg -n -C2 'export .*TestWallet|import .*TestWallet' \
scripts/sanity-sdk/secure-ownable/base-test.ts \
scripts/sanity-sdk/base/BaseSDKTest.ts \
scripts/sanity-sdk/secure-ownable/eip712-signing-tests.tsRepository: PracticalParticle/Bloxchain-Protocol
Length of output: 1551
🏁 Script executed:
# Check if base-test.ts re-exports TestWallet or any exports
rg -n '^export' scripts/sanity-sdk/secure-ownable/base-test.tsRepository: PracticalParticle/Bloxchain-Protocol
Length of output: 189
Fix broken TestWallet import source.
./base-test.ts does not export TestWallet; this import will fail type-check/transpile. Import TestWallet from ../base/BaseSDKTest.ts instead.
🔧 Suggested fix
-import { BaseSecureOwnableTest, TestWallet } from './base-test.ts';
+import { BaseSecureOwnableTest } from './base-test.ts';
+import { TestWallet } from '../base/BaseSDKTest.ts';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/sanity-sdk/secure-ownable/eip712-signing-tests.ts` at line 7, The
import in eip712-signing-tests.ts imports TestWallet from the wrong module;
change the import so TestWallet is imported from the module that actually
exports it (the BaseSDKTest module) instead of './base-test.ts' — update the
import that currently reads "import { BaseSecureOwnableTest, TestWallet } from
'./base-test.ts';" to import BaseSecureOwnableTest and TestWallet from the
module that defines BaseSDKTest/TestWallet.
| // Clear any pending transactions to ensure clean test state (non-fatal if some cannot be cancelled) | ||
| const cleared = await this.clearPendingTransactions(); | ||
| if (!cleared) { | ||
| console.log('⚠️ Proceeding with some pending transactions uncancelled (e.g. meta-tx only)\n'); | ||
| } |
There was a problem hiding this comment.
Fail fast when the suite cannot establish a clean state.
clearPendingTransactions() returns false both for expected leftovers and for real cleanup failures, so swallowing that here can start the suite from an unknown on-chain state and make later results misleading.
Suggested direction
- const cleared = await this.clearPendingTransactions();
- if (!cleared) {
- console.log('⚠️ Proceeding with some pending transactions uncancelled (e.g. meta-tx only)\n');
- }
+ const cleanup = await this.clearPendingTransactions();
+ if (!cleanup.ok) {
+ if (cleanup.reason === 'unsupported_pending_types') {
+ console.log(`⚠️ Proceeding with ${cleanup.remaining} known-unsupported pending transactions\n`);
+ } else {
+ throw new Error(`Failed to establish clean test state: ${cleanup.reason}`);
+ }
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/sanity/secure-ownable/base-test.cjs` around lines 219 - 223, The test
setup currently continues when this.clearPendingTransactions() returns false,
which masks real cleanup failures; change the behavior to fail fast by treating
a false return as an error — in the same initialization/context where
clearPendingTransactions is called, replace the console.log fallback with
throwing an Error (or calling process.exit(1)) including a descriptive message
like "Failed to clear pending transactions: cannot guarantee clean on-chain
state" so the suite aborts immediately when this.clearPendingTransactions()
returns false.
Summary by CodeRabbit
New Features
Breaking Changes
Chores